#include "stdafx.h"
#include "PerformanceControl.h"
#include "DebugMonitor.h"
#include <windows.h>
#include <wchar.h>
#include "Debug.h"


CPerformanceControl* g_ctrl = NULL;

CPerformanceControl::CPerformanceControl()
: m_winDebugMonitor(NULL),
  m_msgCallback(NULL),
  m_notFilter(FALSE)
{
	g_ctrl = this;
}

CPerformanceControl::~CPerformanceControl()
{
	if( m_winDebugMonitor )
	{
		m_winDebugMonitor->Unintialize();
		delete m_winDebugMonitor;
		m_winDebugMonitor = NULL;
	}

	g_ctrl = NULL;
}

BOOL CPerformanceControl::Init()
{
	m_winDebugMonitor = new CWinDebugMonitor();
	m_winDebugMonitor->SetCallback( this->_onMonitorCallback );
	m_winDebugMonitor->Initialize();
	
	::ZeroMemory(&m_cpuFreq, sizeof(LARGE_INTEGER));
	::QueryPerformanceFrequency(&m_cpuFreq);
	::ZeroMemory(&m_startClock, sizeof(LARGE_INTEGER));
	::QueryPerformanceCounter(&m_startClock);

	LaunchProgram(m_executeCmd.c_str());
	return TRUE;
}

/*
double CPerformanceControl::GetSpendTime()
{
	LARGE_INTEGER curClock;
	::ZeroMemory(&curClock, sizeof(LARGE_INTEGER));
	::QueryPerformanceCounter(&curClock);
	return (curClock.QuadPart - m_startClock.QuadPart)/(double)m_cpuFreq.QuadPart;
}
*/

int CPerformanceControl::SetIniPath(LPCWSTR filepath)
{
	FILE* file = NULL;
	errno_t err = _wfopen_s(&file, filepath, L"r");
	if( file == NULL )
		return 0;

	//Execute command
	CHAR str[1024];
	if( fgets(str, 1024, file)!= NULL )
	{
		int len = strlen(str)-1;
		if(str[len] == '\n')
			str[len] = 0;
		m_executeCmd = str;
	}

	//filter list
	while( fgets(str, 1024, file)!= NULL )
	{
		int len = strlen(str)-1;
		if(str[len] == '\n')
			str[len] = 0;
		m_filterList.push_back(str);

		if( str[0]=='*' )
			m_notFilter = TRUE;
	}

	fclose(file);
	return err;
}

BOOL CPerformanceControl::MatchFilterList(const string & str)
{
	if( m_notFilter )
		return TRUE;

	if( str.length()==0 )
		return FALSE;

	vector<string>::iterator iter = m_filterList.begin();
	for( ; iter != m_filterList.end() ; iter++ )
	{
		if( str.find(*iter) != string::npos )
			return TRUE;
	}
	return FALSE;
}

void CPerformanceControl::PushMatchRecord(INT pid, LPCSTR msg)
{
	LARGE_INTEGER curClock;
	::ZeroMemory(&curClock, sizeof(LARGE_INTEGER));
	::QueryPerformanceCounter(&curClock);

	m_recordList.push_back( SMatchRecord(pid, msg, curClock) );
}

void CPerformanceControl::CallbackLastRecord()
{
	if(m_msgCallback)
	{
		INT size = m_recordList.size();
		if(size == 0)
			return;
		
		SMatchRecord* mr = &m_recordList[size-1];
		double timeStamp = (mr->timestamp.QuadPart)/(double)m_cpuFreq.QuadPart;
		double spendTime = 0;
		double totalTime = 0;
		if(size>1)
		{
			spendTime = timeStamp - m_recordList[size-2].timestamp.QuadPart/(double)m_cpuFreq.QuadPart;
			totalTime = (mr->timestamp.QuadPart - m_startClock.QuadPart)/(double)m_cpuFreq.QuadPart;
		}

		CHAR s[1024];
		sprintf(s, "#PID=%d S=%.2lf TS=%.2lf, ST=%.2lf MSG=%s\r\n", mr->pid, timeStamp, totalTime, spendTime, mr->msg.c_str());
		m_msgCallback(s);
	}

}

void CPerformanceControl::_onMonitorCallback(INT pid, LPCSTR msg)
{
	if( g_ctrl && g_ctrl->MatchFilterList(msg) )
	{
		g_ctrl->PushMatchRecord(pid, msg);
		g_ctrl->CallbackLastRecord();
	}
}

BOOL CPerformanceControl::LaunchProgram(LPCSTR szCurrentDir, BOOL bWaitFinish)
{
	HRESULT hr = E_FAIL;
	if( TRUE )//if(PathFileExistsA(szCurrentDir))
	{
		STARTUPINFOA 				StartupInfo;
		PROCESS_INFORMATION	        ProcessInfo;
		ZeroMemory(&StartupInfo, sizeof(StartupInfo));
		ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));

		StartupInfo.cb				= sizeof(StartupInfo);
		StartupInfo.dwFlags			= STARTF_USESHOWWINDOW;
		StartupInfo.wShowWindow	    = SW_SHOW;
		StartupInfo.lpReserved		= NULL;
		StartupInfo.lpDesktop		= NULL;
		StartupInfo.lpTitle			= NULL;
		StartupInfo.cbReserved2		= 0;
		StartupInfo.lpReserved2		= NULL;

		// Use "CreateProcess" instead of "ShellExecuteEx" because ShellExecuteEx may cause a bug that WaitForSingleObject may not work.
		// Some computers return immediately from WaitForSingleObject when the process has not terminated yet.
		// ShellExecuteEx is not stable, and it is recommended that UI should use "CreatePorcess" instead of "ShellExecuteEx".
		CHAR szCommandLine[_MAX_PATH]="";
		strcat(szCommandLine, szCurrentDir);

		dprintf(L"[LaunchProgram] szCommandLine:%s", szCommandLine);
		if (!CreateProcessA(NULL, szCommandLine, NULL, NULL, TRUE, 
				NORMAL_PRIORITY_CLASS, NULL, NULL, &StartupInfo, &ProcessInfo))
		{
			CHAR msg[1024];
			sprintf(msg, "Launch Program Fail! \ncommand line=\n\"%s\"", szCommandLine);
			MessageBoxA(NULL, msg, "Warnning!", MB_OK);

			return FALSE;
		}

		if(bWaitFinish)
		{
			WaitForSingleObject(ProcessInfo.hProcess, INFINITE);

			DWORD dwRet = 1;
			BOOL bRet = GetExitCodeProcess(ProcessInfo.hProcess,
				&dwRet);
			if (bRet && 0 == dwRet)	
			{
				hr = S_OK;
			}
		}
		else
			hr = S_OK;

		CloseHandle(ProcessInfo.hProcess);
		CloseHandle(ProcessInfo.hThread);
	}
	else
	{
		dprintf(L"[LaunchProgram] Program does not exist: %s", szCurrentDir);
	}
	return SUCCEEDED(hr);
}